home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / opndor / ex_ski.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-26  |  20.1 KB  |  601 lines

  1. /* EX_SKI.C - EX_SKI is a simple but addictive door game that is written     */
  2. /*            using OpenDoors. In this action game, the player must control  */
  3. /*            a skier through a downhill slalom course. The user may turn    */
  4. /*            the skier left or right, and the game ends as soon as the      */
  5. /*            player skis outside the marked course. The game begins at      */
  6. /*            an easy level, but quickly becomes more and more difficult     */
  7. /*            as the course to be navigated becomes more and more narrow.    */
  8. /*            The game maintains a list of players with high scores, and     */
  9. /*            this list may be viewed from the main menu.                    */
  10. /*                                                                           */
  11. /*            This program shows how to do the following:                    */
  12. /*                                                                           */
  13. /*               - Maintain a high-score file in a game, in a multi-node     */
  14. /*                 compatible manner.                                        */
  15. /*               - How to use your own terminal control sequences.           */
  16. /*               - How to perform reasonably percise timing under both DOS   */
  17. /*                 and Windows.                                              */
  18.  
  19.  
  20. /* Header file for the OpenDoors API */
  21. #include "opendoor.h"
  22.  
  23. /* Other required C header files */
  24. #include <string.h>
  25. #include <stdio.h>
  26. #include <time.h>
  27. #include <errno.h>
  28. #include <stdlib.h>
  29.  
  30.  
  31. /* Hard-coded configurable constants - change these values to alter game */
  32. #define HIGH_SCORES           15       /* Number of high scores in list */
  33. #define INITIAL_COURSE_WIDTH  30       /* Initial width of ski course */
  34. #define MINIMUM_COURSE_WIDTH  4        /* Minimum width of course */
  35. #define DECREASE_WIDTH_AFTER  100      /* # of ticks before course narrows */
  36. #define CHANGE_DIRECTION      10       /* % of ticks course changes direction */
  37. #define MAX_NAME_SIZE         35       /* Maximum characters in player name */
  38. #define WAIT_FOR_FILE         10       /* Time to wait for access to file */
  39. #define SCORE_FILENAME   "SKIGAME.DAT" /* Name of high score file */
  40.  
  41.  
  42. /* High-score file format structure */
  43. typedef struct
  44. {
  45.    char szPlayerName[MAX_NAME_SIZE + 1];
  46.    DWORD lnHighScore;
  47.    time_t lnPlayDate;
  48. } tHighScoreRecord;
  49.  
  50. typedef struct
  51. {
  52.    tHighScoreRecord aRecord[HIGH_SCORES];
  53. } tHighScoreFile;
  54.  
  55.  
  56. /* Prototypes for functions defined and used in this file */
  57. FILE *OpenAndReadHighScores(tHighScoreFile *pFileContents);
  58. void CloseHighScores(FILE *pfHighScoreFile);
  59. void WriteHighScores(FILE *pfHighScoreFile, tHighScoreFile *pFileContents);
  60. FILE *OpenExclusiveFile(char *pszFileName, char *pszAccess, time_t Wait);
  61. int FileExists(char *pszFileName);
  62. void ShowHighScores(void);
  63. void PlayGame(void);
  64. void SpaceRight(int nColumns);
  65. void MoveLeft(int nColumns);
  66. int AddHighScore(tHighScoreFile *pHighScores, tHighScoreRecord *pScoreRecord);
  67.  
  68.  
  69. /* The main() or WinMain() function: program execution begins here. */
  70. #ifdef ODPLAT_WIN32
  71. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  72.    LPSTR lpszCmdLine, int nCmdShow)
  73. #else
  74. int main(int argc, char *argv[])
  75. #endif
  76. {
  77.    char chMenuChoice;
  78.  
  79. #ifdef ODPLAT_WIN32
  80.    /* In Windows, pass in nCmdShow value to OpenDoors. */
  81.    od_control.od_cmd_show = nCmdShow;
  82. #endif
  83.    
  84.    /* Set program's name for use by OpenDoors. */
  85.    strcpy(od_control.od_prog_name, "Grand Slalom");
  86.    strcpy(od_control.od_prog_version, "Version 6.00");
  87.    strcpy(od_control.od_prog_copyright, "Copyright 1991-1996 by Brian Pirie");
  88.  
  89.    /* Call the standard command-line parsing function. You will probably     */
  90.    /* want to do this in most programs that you write using OpenDoors, as it */
  91.    /* automatically provides support for many standard command-line options  */
  92.    /* that will make the use and setup of your program easer. For details,   */
  93.    /* run the vote program with the /help command line option.               */
  94. #ifdef ODPLAT_WIN32
  95.    od_parse_cmd_line(lpszCmdLine);
  96. #else
  97.    od_parse_cmd_line(argc, argv);
  98. #endif
  99.  
  100.    /* Loop until the user chooses to exit the door */
  101.    do
  102.    {
  103.       /* Clear the screen */
  104.       od_clr_scr();
  105.  
  106.       /* Display program title */
  107.       od_printf("`bright white`                █▀▀ █▀▄ █▀█ █▄ █ █▀▄   █▀▀ █   █▀█ █   █▀█ █▀█▀█\n\r");
  108.       od_printf("`bright red`────────────────`bright white`█▀█`bright red`─`bright white`█▀▄`bright red`─`bright white`█▀█`bright red`─`bright white`█`bright red`─`bright white`▀█`bright red`─`bright white`█");
  109.       od_printf("`bright red`─`bright white`█`bright red`───`bright white`▀▀█`bright red`─`bright white`█`bright red`───`bright white`█▀█`bright red`─`bright white`█`bright red`───`bright white`█");
  110.       od_printf("`bright red`─`bright white`█`bright red`─`bright white`█`bright red`─`bright white`█`bright red`─`bright white`█`bright red`───────────────\n\r");
  111.       od_printf("`bright white`                ▀▀▀ ▀ ▀ ▀ ▀ ▀  ▀ ▀▀    ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀\n\r\n\r");
  112.  
  113.       /* Display instructions */
  114.       od_printf("`dark green`Prepare yourself for the challenge of Grand Slalom downhill skiing!\n\r\n\r");
  115.       od_printf("When `flashing dark green`playing`dark green` the game, press:\n\r");
  116.       od_printf("`dark green`          [`bright green`Q`dark green`] key to ski left\n\r");
  117.       od_printf("          [`bright green`W`dark green`] key to ski right\n\r\n\r");
  118.       od_printf("All that you have to do is ski within the slalom course.\n\r");
  119.       od_printf("It may sound easy - but be warned - it gets harder as you go!\n\r");
  120.       od_printf("(Each time you hear the beep, the course becomes a bit narrower.)\n\r\n\r");
  121.  
  122.       /* Get menu choice from user. */
  123.       od_printf("`bright white`Now, press [ENTER] to begin game, [H] to view High Scores, [E] to Exit: ");
  124.       chMenuChoice = od_get_answer("HE\n\r");
  125.  
  126.       /* Perform appropriate action based on user's choice */
  127.       switch(chMenuChoice)
  128.       {
  129.          case '\n':
  130.          case '\r':
  131.             /* If user chooses to play the game */
  132.             PlayGame();
  133.             break;
  134.  
  135.          case 'H':
  136.             /* If user chose to view high scores */
  137.             ShowHighScores();
  138.             break;
  139.  
  140.          case 'E':
  141.             /* If user chose to return to BBS */
  142.             od_printf("\n\rGoodbye from SKIGAME!\n\r");
  143.             break;
  144.       }
  145.    } while(chMenuChoice != 'E');
  146.  
  147.    /* Exit door at errorlevel 10, and do not hang up */
  148.    od_exit(10, FALSE);
  149.    return(1);
  150. }
  151.  
  152.  
  153. /* OpenAndReadHighScores() - Opens high score file and reads contents. If */
  154. /*                           file does not exist, it is created. File is  */
  155. /*                           locked to serialize access by other nodes on */
  156. /*                           this system.                                 */
  157. FILE *OpenAndReadHighScores(tHighScoreFile *pFileContents)
  158. {
  159.    FILE *pfFile;
  160.    int iHighScore;
  161.  
  162.    /* If high score file does not exist */
  163.    if(!FileExists(SCORE_FILENAME))
  164.    {
  165.       /* Open and create it */
  166.       pfFile = OpenExclusiveFile(SCORE_FILENAME, "wb", WAIT_FOR_FILE);
  167.  
  168.       /* If open was successful */
  169.       if(pfFile != NULL)
  170.       {
  171.          /* Initialize new high score list */
  172.          for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  173.          {
  174.             pFileContents->aRecord[iHighScore].lnHighScore = 0L;
  175.          }
  176.  
  177.          /* Write high score list to the file */
  178.          WriteHighScores(pfFile, pFileContents);
  179.       }
  180.    }
  181.  
  182.    /* If high score file does exit */
  183.    else
  184.    {
  185.       /* Open the existing file */
  186.       pfFile = OpenExclusiveFile(SCORE_FILENAME, "r+b",
  187.                                 WAIT_FOR_FILE);
  188.  
  189.       /* Read the contents of the file */
  190.       if(fread(pFileContents, sizeof(tHighScoreFile), 1, pfFile) != 1)
  191.       {
  192.          /* If unable to read file, then return with an error */
  193.          fclose(pfFile);
  194.          pfFile = NULL;
  195.       }
  196.    }
  197.  
  198.    /* Return pointer to high score file, if avilable */
  199.    return(pfFile);
  200. }
  201.  
  202.  
  203. /* FileExists() - Returns TRUE if file exists, otherwise returns FALSE */
  204. int FileExists(char *pszFileName)
  205. {
  206.    /* Attempt to open the specified file for reading. */
  207.    FILE *pfFile = OpenExclusiveFile(pszFileName, "rb", WAIT_FOR_FILE);
  208.  
  209.    if(pfFile != NULL)
  210.    {
  211.       /* If we are able to open the file, then close it and return */
  212.       /* indicating that it exists.                                */
  213.       fclose(pfFile);
  214.       return(TRUE);
  215.    }
  216.    else
  217.    {
  218.       /* If we are unable to open the file, we proceed as if the file        */
  219.       /* doesn't exist (note that this may not always be a valid assumption) */
  220.       return(FALSE);
  221.    }
  222. }
  223.  
  224.  
  225. /* OpenExclusiveFile() - Opens a file for exclusive access, waiting if the */
  226. /*                       file is not currently available.                  */
  227. FILE *OpenExclusiveFile(char *pszFileName, char *pszAccess, time_t Wait)
  228. {
  229.    FILE *pfFile;
  230.    time_t StartTime = time(NULL);
  231.  
  232.    for(;;)
  233.    {
  234.       /* Attempt to open file */
  235.       pfFile = fopen(pszFileName, pszAccess);
  236.  
  237.       /* If file was opened successfuly, then exit */
  238.       if(pfFile != NULL) break;
  239.  
  240.       /* If open failed, but not due to access failure, then exit */
  241.       if(errno != EACCES) break;
  242.  
  243.       /* If maximum time has elapsed, then exit */
  244.       if(StartTime + Wait < time(NULL)) break;
  245.  
  246.       /* Give the OpenDoors kernel a chance to execute before trying again */
  247.       od_kernel();
  248.    }
  249.  
  250.    /* Return pointer to file, if opened */
  251.    return(pfFile);
  252. }
  253.  
  254.  
  255. /* CloseHighScores() - Closes the high score file, allowing other nodes on */
  256. /*                     system to access it.                                */
  257. void CloseHighScores(FILE *pfHighScoreFile)
  258. {
  259.    if(pfHighScoreFile != NULL)
  260.    {
  261.       fclose(pfHighScoreFile);
  262.    }
  263. }
  264.  
  265.  
  266. /* WriteHighScores() - Writes the information from pFileContents to the */
  267. /*                     high score file.                                 */
  268. void WriteHighScores(FILE *pfHighScoreFile, tHighScoreFile *pFileContents)
  269. {
  270.    if(pfHighScoreFile != NULL)
  271.    {
  272.       fseek(pfHighScoreFile, 0L, SEEK_SET);
  273.       fwrite(pFileContents, sizeof(tHighScoreFile), 1, pfHighScoreFile);
  274.    }
  275. }
  276.  
  277.  
  278. /* ShowHighScores() - Called From DoDoor() to display list of high scores */
  279. void ShowHighScores(void)
  280. {
  281.    FILE *pfFile;
  282.    tHighScoreFile HighScores;
  283.    int iHighScore;
  284.    struct tm *pTimeBlock;
  285.    char szTimeString[34];
  286.  
  287.    /* Clear the screen */
  288.    od_clr_scr();
  289.  
  290.    /* Attempt to read high scores from file */
  291.    pfFile = OpenAndReadHighScores(&HighScores);
  292.    CloseHighScores(pfFile);
  293.  
  294.    if(pfFile == NULL)
  295.    {
  296.       /* If unable to open high score file, display an error message */
  297.       od_printf("`bright red`Unable to access high score file!\n\r");
  298.    }
  299.    else
  300.    {
  301.       /* Display header line */
  302.       od_printf("`bright green`Player                            Score     "
  303.                 "Record Date`dark green`\n\r");
  304.       od_printf("───────────────────────────────────────────────────────────────────────────────\n\r");
  305.  
  306.       /* Display high scores */
  307.       for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  308.       {
  309.          /* Exit loop when we have reached the end of the high scores */
  310.          if(HighScores.aRecord[iHighScore].lnHighScore == 0L) break;
  311.  
  312.          /* Get local time when player set the high score */
  313.          pTimeBlock = localtime(&HighScores.aRecord[iHighScore].lnPlayDate);
  314.          strftime(szTimeString, sizeof(szTimeString),
  315.             "%B %d, %Y at %I:%M%p", pTimeBlock);
  316.  
  317.          /* Display next high score */
  318.          od_printf("%-32.32s  %-8ld  %s\n\r",
  319.                    HighScores.aRecord[iHighScore].szPlayerName,
  320.                    HighScores.aRecord[iHighScore].lnHighScore,
  321.                    szTimeString);
  322.       }
  323.    }
  324.  
  325.    /* Display footer line */
  326.    od_printf("───────────────────────────────────────────────────────────────────────────────\n\r\n\r");
  327.  
  328.    /* Wait for user to press a key */
  329.    od_printf("`bright white`Press [ENTER]/[RETURN] to continue: ");
  330.    od_get_answer("\n\r");
  331. }
  332.  
  333.  
  334. /* PlayGame() - Called from DoDoor() when user chooses to play a game. */
  335. void PlayGame(void)
  336. {
  337.    int nLeftEdge = 1;
  338.    int nRightEdge = nLeftEdge + 1 + INITIAL_COURSE_WIDTH;
  339.    int nPlayerPos = nLeftEdge + 1 + (INITIAL_COURSE_WIDTH / 2);
  340.    long lnScore = 0;
  341.    int nDistanceSinceShrink = 0;
  342.    int bMovingRight = TRUE;
  343.    char cKeyPress;
  344.    tHighScoreRecord ScoreRecord;
  345.    FILE *pfFile;
  346.    tHighScoreFile HighScores;
  347.    int nBackup;
  348.    clock_t StartClock;
  349.  
  350.    /* Clear the Screen */
  351.    od_set_color(L_WHITE, B_BLACK);
  352.    od_clr_scr();
  353.  
  354.    /* Set current display colour to white */
  355.    od_set_attrib(L_WHITE);
  356.  
  357.    /* Re-seed random number generator */
  358.    srand((unsigned int)time(NULL));
  359.  
  360.    /* Loop until game is over */
  361.    for(;;)
  362.    {
  363.       StartClock = clock();
  364.  
  365.       /* Display current line */
  366.       if(od_control.user_ansi || od_control.user_avatar)
  367.       {
  368.          SpaceRight(nLeftEdge - 1);
  369.          od_set_color(L_WHITE, D_RED);
  370.          od_putch((char)223);
  371.          od_repeat((unsigned char)219, 
  372.             (unsigned char)(nPlayerPos - nLeftEdge - 1));
  373.          od_putch((char)254);
  374.          od_repeat((unsigned char)219,
  375.             (unsigned char)(nRightEdge - nPlayerPos - 1));
  376.          od_putch((char)223);
  377.          nBackup = nRightEdge - nPlayerPos + 1;
  378.       }
  379.       else
  380.       {
  381.          /* If neither ANSI nor AVATAR modes are active, then display */
  382.          /* course using plain-ASCII.                                 */
  383.          SpaceRight(nLeftEdge - 1);
  384.          od_putch((char)(bMovingRight ? '\\' : '/'));
  385.          SpaceRight(nPlayerPos - nLeftEdge - 1);
  386.          od_putch('o');
  387.          SpaceRight(nRightEdge - nPlayerPos - 1);
  388.          od_putch((char)(bMovingRight ? '\\' : '/'));
  389.       }
  390.  
  391.       /* Loop for each key pressed by user */
  392.       while((cKeyPress = (char)od_get_key(FALSE)) != '\0')
  393.       {
  394.          if(cKeyPress == 'q' || cKeyPress == 'Q')
  395.          {
  396.             /* Move left */
  397.             --nPlayerPos;
  398.          }
  399.          else if(cKeyPress == 'w' || cKeyPress == 'W')
  400.          {
  401.             /* Move right */
  402.             ++nPlayerPos;
  403.          }
  404.       }
  405.  
  406.       /* Check whether course should turn */
  407.       if((rand() % 100) < CHANGE_DIRECTION)
  408.       {
  409.          bMovingRight = !bMovingRight;
  410.       }
  411.       else
  412.       {
  413.          /* If no change in direction, then position moves */
  414.          /* Adjust course position appropriately */
  415.          if(bMovingRight)
  416.          {
  417.             ++nLeftEdge;
  418.             ++nRightEdge;
  419.          }
  420.          else
  421.          {
  422.             --nLeftEdge;
  423.             --nRightEdge;
  424.          }
  425.       }
  426.  
  427.       /* Check whether course size should shink */
  428.       if(++nDistanceSinceShrink >= DECREASE_WIDTH_AFTER)
  429.       {
  430.          /* Reset distance */
  431.          nDistanceSinceShrink = 0;
  432.  
  433.          /* Randomly choose a side to shrink */
  434.          if((rand() % 100) < 50)
  435.          {
  436.             ++nLeftEdge;
  437.          }
  438.          else
  439.          {
  440.             --nRightEdge;
  441.          }
  442.  
  443.          /* Beep when we shrink the size. */
  444.          od_printf("\a");
  445.       }
  446.  
  447.       /* Change course direction if it collides with edge of screen */
  448.       if(nLeftEdge < 1)
  449.       {
  450.          bMovingRight = TRUE;
  451.          ++nLeftEdge;
  452.          ++nRightEdge;
  453.       }
  454.       else if(nRightEdge > 79)
  455.       {
  456.          bMovingRight = FALSE;
  457.          --nLeftEdge;
  458.          --nRightEdge;
  459.       }
  460.  
  461.       /* Check that player is still within the course */
  462.       if(nPlayerPos <= nLeftEdge || nPlayerPos >= nRightEdge)
  463.       {
  464.          /* Player has left course - game over! */
  465.          od_set_color(D_GREY, D_BLACK);
  466.          od_clr_scr();
  467.          od_printf("`bright red`       !!! Game Over !!!\n\r\n\r");
  468.          od_printf("`dark green`You have veered off the course!\n\r\n\r");
  469.          od_printf("Your Score is: %ld\n\r", lnScore);
  470.  
  471.          /* Create a score record */
  472.          ScoreRecord.lnHighScore = lnScore;
  473.          strncpy(ScoreRecord.szPlayerName, od_control.user_name, MAX_NAME_SIZE);
  474.          ScoreRecord.szPlayerName[MAX_NAME_SIZE] = '\0';
  475.          ScoreRecord.lnPlayDate = time(NULL);
  476.  
  477.          /* Attempt to read high scores from file */
  478.          pfFile = OpenAndReadHighScores(&HighScores);
  479.  
  480.          if(pfFile == NULL)
  481.          {
  482.             /* If unable to open high score file, display an error message */
  483.             od_printf("`bright red`Unable to access high score file!\n\r");
  484.          }
  485.          else
  486.          {
  487.             /* Check whether user made it to high score list */
  488.             if(AddHighScore(&HighScores, &ScoreRecord))
  489.             {
  490.                od_printf("Congratulations! You have made it to the high score list!\n\r");
  491.                /* If so, write the new high score list */
  492.                WriteHighScores(pfFile, &HighScores);
  493.             }
  494.  
  495.             /* Close and unlock file */
  496.             CloseHighScores(pfFile);
  497.          }
  498.  
  499.          /* Wait for user to press enter */
  500.          od_printf("`bright white`\n\rPress [ENTER]/[RETURN] to return to menu: ");
  501.          od_get_answer("\n\r");
  502.  
  503.          return;
  504.       }
  505.  
  506.       /* Delay for about 1/18th of a second, to add a constant delay after */
  507.       /* each line is displayed that does not depend on the connect speed. */
  508.       while(clock() < StartClock + (((clock_t)CLOCKS_PER_SEC) / 18))
  509.          od_sleep(0);
  510.  
  511.       /* Increase score */
  512.       ++lnScore;
  513.  
  514.       /* Replace skiier character with track character */
  515.       if(od_control.user_ansi)
  516.       {
  517.          MoveLeft(nBackup);
  518.          od_set_color(L_WHITE, D_GREY);
  519.          od_putch((char)178);
  520.          od_set_color(L_WHITE, B_BLACK);
  521.       }
  522.  
  523.       /* Move to next line */
  524.       od_printf("\r\n");
  525.    }
  526. }
  527.  
  528.  
  529. /* SpaceRight() - Moves right the specified number of columns. In ANSI mode, */
  530. /*                uses the move cursor right control sequence. Otherwise,    */
  531. /*                uses od_repeat(), which is optimized for ASCII and AVATAR  */
  532. /*                modes.                                                     */
  533. void SpaceRight(int nColumns)
  534. {
  535.    char szSequence[6];
  536.  
  537.    /* If we don't have a positive column count, then return immediately */
  538.    if(nColumns <= 0) return;
  539.  
  540.    /* If operating in ANSI mode */
  541.    if(od_control.user_ansi)
  542.    {
  543.       /* Move cursor right using ESC[nC control sequence */
  544.       sprintf(szSequence, "\x1b[%02.2dC", nColumns);
  545.       od_disp_emu(szSequence, TRUE);
  546.    }
  547.  
  548.    /* If not operating in ANSI mode */
  549.    else
  550.    {
  551.       od_repeat(' ', (unsigned char)nColumns);
  552.    }
  553. }
  554.  
  555.  
  556. /* MoveLeft() - Moves the cursor right the specified number of columns. */
  557. /*              Intended for use in ANSI mode only.                     */
  558. void MoveLeft(int nColumns)
  559. {
  560.    /* Move cursor left using ESC[nD control sequence */
  561.    char szSequence[6];
  562.    sprintf(szSequence, "\x1b[%02.2dD", nColumns);
  563.    od_disp_emu(szSequence, TRUE);
  564. }
  565.  
  566.  
  567. /* AddHighScore() - Adds a new score to the high score list, if it is high   */
  568. /*                  enough. Returns TRUE if score is added, FALSE otherwise. */
  569. int AddHighScore(tHighScoreFile *pHighScores, tHighScoreRecord *pScoreRecord)
  570. {
  571.    int iHighScore;
  572.    int iExistingScore;
  573.  
  574.    /* Loop through each existing high score */
  575.    for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  576.    {
  577.       /* If new score is greater than or equal to this one, then its */
  578.       /* position has been found.                                    */
  579.       if(pHighScores->aRecord[iHighScore].lnHighScore <=
  580.          pScoreRecord->lnHighScore)
  581.       {
  582.          /* Move remaining scores down one in list */
  583.          for(iExistingScore = HIGH_SCORES - 1; iExistingScore >= iHighScore + 1;
  584.           --iExistingScore)
  585.          {
  586.             pHighScores->aRecord[iExistingScore] =
  587.                pHighScores->aRecord[iExistingScore - 1];
  588.          }
  589.  
  590.          /* Add new score to list */
  591.          pHighScores->aRecord[iHighScore] = *pScoreRecord;
  592.  
  593.          /* Return with success */
  594.          return(TRUE);
  595.       }
  596.    }
  597.  
  598.    /* Score did not make it to list */
  599.    return(FALSE);
  600. }
  601.